

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
function require(url) {
    let scope = {};
    Cu.import(url, scope);
    return scope;
}
let {Prefs} = require("chrome://jsm/content/Preferences.js");
let {MiscUtils, StrUtils} = require("chrome://jsm/content/utils.js");
let {FIO} = require("chrome://jsm/content/FileHelpers.js");
let {Services} = require("resource://gre/modules/Services.jsm");
let {OS} = require("resource://gre/modules/osfile.jsm");
let {Task} = require("resource://gre/modules/Task.jsm");

var g_tree = null;


function MacrosTree(tree, children_id, cur_wnd) {
    this.tree = tree;
    this.wnd = cur_wnd;
    this.children_id = children_id;

    return this;
}



function compare(a, b) {
    var la = a.toLowerCase(), lb = b.toLowerCase();
    var bound = Math.min(la.length, lb.length);

    for (var i = 0; i < bound; i++) {
        var l = la.charAt(i), r = lb.charAt(i), x;
        if (l == r)
            continue;
        
        if (l == "#")
            return -1;
        else if (r == "#")
            return 1;
        else if (x = l.localeCompare(r))
            return x;
    }

    return la.length - lb.length; 
}


MacrosTree.prototype = {
    tree: null,
    children_id: null,
    wnd: null,

    get children() {
	return this.doc.getElementById(this.children_id);
    },

    get doc() {
	return this.wnd.document;
    },

    getLabelFromItem(item) {
	var child = item.firstChild;
	while(child) {
	    if(child.tagName == "treerow" ) {
		if(child.firstChild.tagName == "treecell") {
		    var label = child.firstChild.getAttribute("label");
		    return label;
		}
	    }
	    child = child.nextSibling;
	}
	return null;
    },

    treechildrenFromItem(item) {
	var child = item.firstChild;
	while(child) {
	    if(child.tagName == "treechildren" ) {
		return child;
	    }
	    child = child.nextSibling;
	}
	return null;
    },

    
    
    getContainer(name, parent) {
	var items = parent.childNodes;
	for (var i = 0; i < items.length; i++) {
	    if( items.item(i).getAttribute("container") != "true" )
		continue; 	
	    var item = items.item(i);
	    if (this.getLabelFromItem(item) == name )
		return this.treechildrenFromItem(item);
	}
	
	
	var item = this.doc.createElement("treeitem");
	item.setAttribute("container", "true");

	var row = this.doc.createElement("treerow");
	var con = this.doc.createElement("treechildren");
	item.appendChild(row);
	item.appendChild(con);

	var cell = this.doc.createElement("treecell");
	cell.setAttribute("label", name);

	row.appendChild(cell);

	var child = parent.firstChild;
	while(child) {
	    if (child.getAttribute("container") == "true") {
		var li = this.getLabelFromItem(item).toLowerCase();
		var ci = this.getLabelFromItem(child).toLowerCase();
		if (li < ci) {
		    parent.insertBefore(item, child);
		    break;
		}
	    } else {
		parent.insertBefore(item, child);
		break;
	    }
	    child = child.nextSibling;
	}
	if (!child) {
	    parent.appendChild(item);
	}

	return con;
    },


    
    insertLeaf(name, parent) {
	var src, match, properties = "";
	if (match = name.match(/\.(js|iim)$/i)) {
	    
	    if (match[1] == "js") {
		properties = "js-file";
	    } else if (match[1] == "iim") {
		properties = "iim-file";
	    }
	} else {
	    
	    return;
	}

	var item = this.doc.createElement("treeitem");
	var row = this.doc.createElement("treerow");
	var cell = this.doc.createElement("treecell");
	cell.setAttribute("properties", properties);
	cell.setAttribute("label", name);
	row.appendChild(cell);
	item.appendChild(row);

	var child = parent.firstChild;
	while(child) {
	    if (child.getAttribute("container") != "true") {
		var li = this.getLabelFromItem(item).toLowerCase();
		var ci = this.getLabelFromItem(child).toLowerCase();
		if (compare(li, ci) < 0) {
		    parent.insertBefore(item, child);
		    break;
		}
	    }
	    child = child.nextSibling;
	}
	if (!child) {
	    parent.appendChild(item);
	}

    },

    
    initializeTree(entries) {
	
	while (this.children.hasChildNodes()) {
	    var nodes = this.children.childNodes;
	    for(var i = 0; i < nodes.length; i++) {
		this.children.removeChild(nodes.item(i));
	    }
	}
	this.tree.treeBoxObject.invalidate();
	for (let entry of entries) {
	    let path = OS.Path.split(entry).components;
            let con = this.children;
	    
	    for (let x = 0; x < path.length-1; x++) {
		con = this.getContainer(path[x], con);
	    }
            this.insertLeaf(OS.Path.basename(entry), con);
	}
    },

    
    findItem(name) {
	if(name == "")
	    return this.children.parentNode; 
	var con = this.children;
	var path = name.split(FIO.pathSeparator);

	for(var i = 0; i < path.length; i++) {
            let item = con.firstChild;
	    while (item) {
		if (this.getLabelFromItem(item) == path[i]) {
		    var tcon = this.treechildrenFromItem(item);
		    if (i == path.length-1) {
			
			return item;
		    } else if (path[path.length-1] == "" &&
                               i == path.length-2) {
			
			return item;
		    } else if (!tcon) { 
			return null;
		    } else {
			con = tcon;
			break;
		    }
		}
                item = item.nextSibling
	    }
	    if (!item) 		
		return null;
	}

	return null;
    },

    
    getItemAt(idx) {
	var ret = {isContainer: false, DOMNode: null, path: null};

	if ( this.tree.view.isContainer(idx) ) {
	    ret.isContainer = true;
	}

	
	var cidx = idx;
	var s = "";
	while(this.tree.view.getLevel(cidx) > 0) {
	    var item = this.tree.contentView.getItemAtIndex(cidx);
	    if ( this.tree.view.isContainer(cidx) ) {
		s = this.getLabelFromItem(item) + FIO.pathSeparator + s;
	    } else {
		s = this.getLabelFromItem(item) + s;
	    }
	    cidx = this.tree.view.getParentIndex(cidx);
	}

	ret.path = s;
	
	ret.DOMNode = this.tree.contentView.getItemAtIndex(idx);

	return ret;
    },

    getIndexOfItem(item) {
	return this.tree.contentView.getIndexOfItem(item);
    },

    getItemPath(item) {
	var idx = this.getIndexOfItem(item);
	if (idx == -1)
	    return null;
	var node = this.getItemAt(idx);
	if (!node)
	    return null;
	return node.path;
    },

    
    getSelectedItem() {
	var idx = this.tree.currentIndex;
	if ( idx == -1 || !this.tree.view.selection.isSelected(idx))
	    return null;
	return this.getItemAt(idx);
    },

    insertItem(item, path) {
	var con = this.findItem(path);
	if (!con || con.getAttribute("container") != "true")
	    return null;	
	if ( !(con = this.treechildrenFromItem(con)) )
	    return null;
	var child = con.firstChild;
	while(child) {
	    if (item.getAttribute("container") == "true") {
		if (child.getAttribute("container") == "true") {
		    if (this.getLabelFromItem(item) <
                        this.getLabelFromItem(child)) {
			con.insertBefore(item, child);
			break;
		    }
		} else {
		    con.insertBefore(item, child);
		    break;
		}
	    } else {
		if (child.getAttribute("container") != "true") {
                    var li = this.getLabelFromItem(item).toLowerCase();
                    var ci = this.getLabelFromItem(child).toLowerCase();

		    if (compare(li, ci) < 0) {
			con.insertBefore(item, child);
			break;
		    }
		}
	    }
	    child = child.nextSibling;
	}
	if (!child) {
	    con.appendChild(item);
	}

	return item;
    },

    removeItem(path) {
	var item = this.findItem(path);
	if (!item)
	    return null;
	var con = item.parentNode; 
	return con.removeChild(item);
    },

    _treewalker(item, arr, cur_path) {
        if (item.getAttribute("container") == "true") {
            cur_path += this.getLabelFromItem(item)+FIO.pathSeparator;
            var tc = this.treechildrenFromItem(item);
            if ( tc.hasChildNodes() ) {
        	var child = tc.firstChild;
        	while(child) {
        	    arr = this._treewalker(child, arr, cur_path);
        	    child = child.nextSibling;
        	}
            } else {
        	arr.push(cur_path);
            }
        } else {
            arr.push(cur_path + this.getLabelFromItem(item));
        }
        return arr;
    },


    getItemNamesArray() {
	var arr = new Array();
        if ( this.children.hasChildNodes() ) {
            var item = this.children.firstChild;
            while(item) {
                arr = this._treewalker(item, arr, "");
                item = item.nextSibling;
            }
        }
        return arr;
    },

    sortItems(sort_func) {
	var arr = this.getItemNamesArray();
	arr.sort(sort_func);
	this.initializeTree(arr);
    },

    queryState() {
        var rv = {unfoldedItems:  new Object(), selectedItem: null};
        
        for(var idx = 0; idx < this.tree.view.rowCount; idx++) {
            if (this.tree.view.isContainer(idx) &&
                this.tree.view.isContainerOpen(idx)) {
                var x = this.getItemAt(idx);
                rv.unfoldedItems[x.path] = true;
            }
        }
        
        var x = this.getSelectedItem();
        if (x != null) {
            rv.selectedItem = x.path;
        }
        return rv;
    },

    applyState(state) {
        for (var path in state.unfoldedItems) {
            for(var i = 0; i < this.tree.view.rowCount; i++) {
                if (this.tree.view.isContainer(i)) {
                    var x = this.getItemAt(i);
                    var is_open = this.tree.view.isContainerOpen(i);
                    if (path == x.path && !is_open) {
                        x.DOMNode.setAttribute("open", "true");
                    }
                }
            }
        }
        
        if (state.selectedItem) {
            for(var i = 0; i < this.tree.view.rowCount; i++) {
                var x = this.getItemAt(i);
                if (x.path == state.selectedItem) {
                    this.tree.treeBoxObject.ensureRowIsVisible(i);
                    this.tree.view.selection.select(i);
                    this.tree.view.selectionChanged();
                    this.tree.view.selection.currentIndex = i;
                    break;
                }
            }
        }
    }
};


function getMTreeObject() {
    return g_tree;
}





function getLastPathNode(path) {
    return OS.Path.basename(path);
}


function showPathErrorDialog(path) {
    var treeview = document.getElementById("treeview");
    var errdlg = document.getElementById("path-error-dialog");
    var errtxt = document.getElementById("path-error-text");
    var deck = document.getElementById("tree-box-deck");
    errtxt.textContent =
        errtxt.textContent.replace("$path$", "\""+path+"\"");
    deck.selectedIndex = 2;
}


function correctPathSettings() {
    var treeview = document.getElementById("treeview");
    var errdlg = document.getElementById("path-error-dialog");
    mainwindow.iMacros.showPrefDialog("path-settings");
    var deck = document.getElementById("tree-box-deck");
    deck.selectedIndex = 0;
    initMTree();
}


function iterateThrough(path, entries) {
    let it = new OS.File.DirectoryIterator(path);
    return Task.spawn(function *() {
        while(true) {
            let entry = yield it.next();
            if (entry.isDir) {
                entries.push(entry);
                yield iterateThrough(entry.path, entries);
            } else {
                let name = entry.name.toLowerCase();
                if (name.endsWith(".iim") || name.endsWith(".js"))
                    entries.push(entry);
            }
        }
    }).catch((e) => {
        it.close();
        if (e != StopIteration)
            throw e;
    });
}


function getMacroEntries(path) {
    return OS.File.exists(path).then((exists) => {
        if (!exists) {
            showPathErrorDialog(path);
            return Promise.reject(new Error("Path "+path+" does not exist"));
        } else
            return Promise.resolve();
    }).then(() => {
        let entries = new Array();
        return iterateThrough(path, entries)
            .then(() => Promise.resolve(entries));
    })
}




function onDragStart(event) {
    
    var mtree = getMTreeObject();
    var idx = mtree.tree.treeBoxObject.
        getRowAt(event.pageX, event.pageY);
    if (idx == -1 || idx == 0)
	return;
    var item = mtree.getSelectedItem();
    if(idx != mtree.getIndexOfItem(item.DOMNode))
	return;
    event.dataTransfer.setData('text/plain', item.path)
    event.dataTransfer.effectAllowed = "move";
}


function onDragOver (event) {
    var mtree = getMTreeObject();
    var idx = mtree.tree.treeBoxObject.
        getRowAt(event.pageX, event.pageY);
    var canDrop = false;
    if (idx != -1) {
        var dstNode = mtree.getItemAt(idx);
        canDrop = dstNode && dstNode.isContainer &&
            event.dataTransfer.types.contains("text/plain");
    }
    
    if (canDrop) {
        event.dropEffect = "move";
        event.preventDefault();
    }
}



function onDragDrop (event) {
    if (!event.dataTransfer.types.contains("text/plain"))
        return;
    var src_path = event.dataTransfer.getData("text/plain");

    
    var mtree = getMTreeObject();
    var idx = mtree.tree.treeBoxObject.
	getRowAt(event.pageX, event.pageY);
    if (idx == -1)
	return;
    var dstNode = mtree.getItemAt(idx);
    if (!dstNode)
	return;

    let dst_path, sep = FIO.pathSeparator;
    if (dstNode.isContainer) {
	dst_path = dstNode.path;
    } else {
	if (dstNode.path.indexOf(sep) != -1)
	    dst_path = dstNode.path.split(sep).slice(0, -1).join(sep);
	else
	    dst_path = "";
    }

    
    let src_is_dir = src_path.endsWith(sep);
    let src = FIO.getMacroPath(src_path);
    let dst = OS.Path.join(FIO.getMacroPath(dst_path), OS.Path.basename(src));
    (src_is_dir? FIO.moveDirectory(src, dst):
     OS.File.move(src, dst)).then(() => {
        
	var srcNode = mtree.removeItem(src_path);
	if (!srcNode)
	    return;
	mtree.insertItem(srcNode, dst_path);
    }).catch(Cu.reportError);
    
    event.preventDefault();

}


function setDragHandlers() {
    var tree = document.getElementById("treeview");
    tree.addEventListener("dragstart", onDragStart, false);
    tree.addEventListener("dragenter", onDragOver, false);
    tree.addEventListener("dragover", onDragOver, false);
    tree.addEventListener("drop", onDragDrop, false);
};






function initMTree() {
    
    let predicate = function(a, b) {
        if(!a.isDir && !b.isDir) {
            return compare(a.name, b.name);
        } else if (a.isDir && !b.isDir) {
	    return -1; 		
        } else if (!a.isDir && b.isDir) {
	    return 1;
        } else {
	    return compare(a.name, b.name);
        }
    };
    let path = Prefs.defaultMacroPath;
    let base_path_length = path.length;
    return getMacroEntries(path).then(entries => {
        entries.sort(predicate);
        let tree = document.getElementById("treeview");
        if (!tree)
	    throw "no treeview was found";
        if (!g_tree)
	    g_tree = new MacrosTree(tree, "favtree", window);
        g_tree.initializeTree(entries.map(
            entry => entry.path.substring(base_path_length+1)+
                (entry.isDir ? FIO.pathSeparator : "")
        ));
    }).catch(Cu.reportError);
}


function checkSIActive() {
    if (!MiscUtils.isWindows())
        return;
    
    
    
	  
    
    
    
    
    try {
        
        let wrk = Cc["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Ci.nsIWindowsRegKey);
        wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, "SOFTWARE", wrk.ACCESS_READ);
        if (wrk.hasChild("iMacros")) {
            var subkey = wrk.openChild("iMacros", wrk.ACCESS_READ);
            if (subkey.hasValue("PathBasic")) { 
                
                
                if (subkey.hasValue("InterfaceType") &&
                    /^fx/.test(subkey.readStringValue("InterfaceType"))) {
                    
                    
                }
            }
            subkey.close();
        }

        wrk.close();

        
        wrk.create(wrk.ROOT_KEY_CURRENT_USER,
                   "Software\\iMacros",
                   wrk.ACCESS_WRITE);
        wrk.writeStringValue("fx", imacros_version);
        wrk.close();
    } catch (e) {
        Cu.reportError(e);
    }
}

function setAdDetails() {
	let ad_text = document.getElementById("imacros-ad-text");
	let ad_text_container = document.getElementById("imacros-ad-text-container");
	let ad_image = document.getElementById("imacros-ad-image");
	
	var xmlhttp = new XMLHttpRequest();
	var url = "chrome://imacros/skin/ads.json";

	xmlhttp.onreadystatechange = function() {
		if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
			var ads = JSON.parse(xmlhttp.responseText);
			var ad_index = Math.floor(Math.random() * ads.length);
			
			ad_text.value = ads[ad_index].ad_text;
			
			ad_text.href = ads[ad_index].ad_link;

			

			ad_image.onclick = function(){
									mainwindow.iMacros.addTab(ad_text.href);
							   };
			ad_image.src = "chrome://imacros/skin/ads/" + ads[ad_index].ad_img;
			
			ad_text_container.collapsed = false;
		}
	};
	xmlhttp.open("GET", url, true);
	xmlhttp.send();
}

function MTree_onSelect(event) {
    if (mainwindow) {
	mainwindow.iMacros.panel.updateSidebarState();
    }
}


function MTree_onDblClick(event) {
    var idx = g_tree.tree.boxObject.getRowAt(event.clientX, event.clientY);
    var item = g_tree.getItemAt(idx);
    if (item.isContainer)
        return;
    if (mainwindow) {
	mainwindow.iMacros.play();
    }
}





function MPopup_onShowing(event) {
    
    var mtree = getMTreeObject();
    var item = mtree.getSelectedItem();
    if (!item) {
        event.preventDefault();
        return;
    }     
               
    var bookmark = document.getElementById('addbookmark');
    var editmacro = document.getElementById('editmacro');
    if (item.isContainer) {
	bookmark.hidden = true;
        editmacro.hidden = true;
    } else {
	bookmark.hidden = false;
        editmacro.hidden = false;
    }
}



function MPopup_removeItem() {
    let mtree = getMTreeObject();
    let item = mtree.getSelectedItem();
    if (!item)
	return;
    let path = FIO.getMacroPath(item.path);
    if (!confirm(item.isContainer ?
		 MiscUtils.strings("imacrosareyousurewant37"):
		 MiscUtils.strings("imacrosareyousurewant38")))
	return;
    let remove_promise = item.isContainer ? 
        OS.File.removeDir(path) :
        OS.File.remove(path);
    remove_promise.then(() => mtree.removeItem(item.path))
        .catch(Cu.reportError);
}


function MPopup_renameItem() { 
    var mtree = getMTreeObject();
    var item = mtree.getSelectedItem();
    if (!item)
	return;
    var editbox = document.getElementById('editname');
    var renamebtn = document.getElementById('im-rename-button');
    editbox.collapsed = null;
    editbox.value = mtree.getLabelFromItem(item.DOMNode);
    renamebtn.collapsed = null;
    editbox.focus();
    editbox.select();
}



function MPopup_CreateFolder() {  
    var mtree = getMTreeObject();
    var item = mtree.getSelectedItem();
    if (!item)
	return;
    var con, idx;
    if (item.isContainer) {
	con = mtree.treechildrenFromItem(item.DOMNode);
        idx = mtree.getIndexOfItem(item.DOMNode);
        if (!mtree.tree.view.isContainerOpen(idx))
            mtree.tree.view.toggleOpenState(idx);
    } else {
	con = item.DOMNode.parentNode;
    }

    
    var basename = "New Folder";
    var name = basename;
    if (con.hasChildNodes()) {
	var nodes = con.childNodes, found = true, count = 1;
	while (found) {
	    found = false;
	    for (var i = 0; i < nodes.length; i++) {
		if (mtree.getLabelFromItem(nodes.item(i)) == name) {
		    found = true;
		    name = basename+" ("+count+")";
		    count++;
		    break;
		}
	    }
	}
    }

    let newcon = mtree.getContainer(name, con);
    if (!newcon) {
	return;
    }
    idx = mtree.tree.contentView.getIndexOfItem(newcon.parentNode);
    let t = mtree.getItemAt(idx);
    let path = FIO.getMacroPath(t.path);
    OS.File.makeDir(path, {from: Prefs.defaultMacroPath})
        .catch(Cu.reportError);
}


function MPopup_AddBookmark(type) {
    mainwindow.iMacros.addBookmark(type);
}



function MPopup_EditMacro() {
    var mtree = getMTreeObject();
    var item = mtree.getSelectedItem();
    if (!item || item.isContainer)
        return;
    var macro = {name: mtree.getLabelFromItem(item),
                 path: item.path};
    mainwindow.iMacros.edit(macro);
}




function onEditKeypress(evt) {
    const VK_ENTER = Ci.nsIDOMKeyEvent.DOM_VK_ENTER;
    const VK_RETURN = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;

    if (evt.keyCode == VK_RETURN || evt.keyCode == VK_ENTER) {
        MTree_renameItem();
        evt.preventDefault();
    }
}



function MTree_renameItem() { 
    let mtree = getMTreeObject();
    let item = mtree.getSelectedItem();
    if (!item)
	return;
    let editbox = document.getElementById('editname');
    let button = document.getElementById('im-rename-button');
    let ext = item.path.match(/\.(js|iim)$/);
    if (ext) 
	ext = ext[1];
    let newname = editbox.value;
    if (!newname) {
	editbox.collapsed = true;
	button.collapsed = true;
	return;
    }
    if (!/\.(js|iim)/i.test(newname) && ext)
	newname += "."+ext;
    let src = FIO.getMacroPath(item.path);
    let dst = OS.Path.join(
        Prefs.defaultMacroPath,
            ...OS.Path.split(item.path).components
            .slice(0, item.isContainer? -2 : -1), newname);
    (!item.isContainer? OS.File.move(src, dst) :
     FIO.moveDirectory(src, dst)).then(() => {
	 mtree.tree.view.setCellText(
             mtree.tree.currentIndex,
	     mtree.tree.columns.getFirstColumn(),
	     newname
         );
         editbox.collapsed = true;
	 button.collapsed = true;
     }).catch(Cu.reportError);
}


window.addEventListener("load", function() {
    
    initMTree().then(() => {
        
        setDragHandlers();
        
        checkSIActive();
		setAdDetails();
        
        Services.obs.notifyObservers(window, "imacros-sidebar-loaded", null);
    });
}, false);



window.addEventListener("unload", function() {
    
    let state = g_tree.queryState();
    Services.obs.notifyObservers(mainwindow, "imacros-sidebar-closed", state);
}, false);